Retrieve gRPC server.address/server.port from gRPC target#16161
Draft
trask wants to merge 11 commits into
Draft
Conversation
4828a53 to
c45ee61
Compare
c45ee61 to
ce22789
Compare
laurit
reviewed
Feb 17, 2026
laurit
reviewed
Feb 17, 2026
laurit
approved these changes
Feb 17, 2026
9a1c3d7 to
2a0e0b9
Compare
laurit
reviewed
Feb 25, 2026
0f60636 to
dd4d89f
Compare
d93c7e4 to
e19f9d2
Compare
e19f9d2 to
9ae10a5
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds support for deriving server.address / server.port for gRPC client spans from the channel “target” string (when available), aligning with emerging RPC stable semantic conventions and improving accuracy beyond authority()-only parsing.
Changes:
- Introduces internal parsing utilities (
GrpcTargetParser/ParsedTarget) and wires them into client request attribute extraction. - Adds a new recommended library API
GrpcTelemetry#addClientInterceptor(ManagedChannelBuilder)that (on gRPC 1.64+) captures the channel target viaInternalManagedChannelBuilder.interceptWithTarget. - Updates javaagent + Armeria gRPC javaagent instrumentation to pass through target/authority where available (gated by stable RPC semconv opt-in).
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParserTest.java | Adds unit coverage for parsing various gRPC target formats into address/port. |
| instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java | Updates tests to use the new addClientInterceptor API. |
| instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java | Updates streaming tests to use the new addClientInterceptor API. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java | Switches server request creation to GrpcRequest.createServerRequest. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java | Plumbs an optional target through interceptor construction and uses it to enrich client request attributes. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/ParsedTarget.java | Adds a small internal value type to carry parsed address/port. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/Internal.java | Adds an internal bridge for javaagent/library coordination when creating interceptors with target info. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParser.java | Implements parsing of gRPC target strings into address/port. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java | Uses new GrpcRequest server address/port accessors for client network attributes. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java | Adds addClientInterceptor with reflective gRPC 1.64+ target capture; deprecates createClientInterceptor. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java | Refactors request to store server address/port explicitly and adds client/server factory methods. |
| instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java | Uses new GrpcRequest server address/port accessors for server network attributes. |
| instrumentation/grpc-1.6/library/README.md | Updates usage docs to recommend addClientInterceptor. |
| instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java | Reworks client interceptor creation to allow passing a target string. |
| instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java | Captures builder target (when stable semconv enabled) and creates a target-aware interceptor. |
| instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java | Passes URI authority as the target (when stable semconv enabled) for target-aware interceptor creation. |
# Conflicts: # instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java # instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java # instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java # instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcServerNetworkAttributesGetter.java # instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java
…ry proxy Previously the InvocationHandler forwarded any non-newInterceptor call to the ManagedChannelBuilder instance, which gave Object methods (equals/hashCode/ toString) misleading semantics — e.g. proxy.equals(builder) would return true. Handle equals/hashCode/toString explicitly with identity semantics, and reject any other unexpected method.
The GrpcRequest hostFromAuthority/portFromAuthority helpers split on the first ':', which mangles IPv6 authorities like '[::1]:8080' (host becomes '[' and the port parse fails). This is now both the fallback path for clients on gRPC < 1.64 and the primary path for server-side requests. Promote the bracket-aware parsing logic from GrpcTargetParser into a new parseAuthority() helper and route both client fallback and server request construction through it, so authority and target parsing share a single source of truth.
Previously parseHostPort("") returned a ParsedTarget with an empty
address, which could lead to emitting server.address="" on spans for
targets like "dns:" or "dns:///". Return null instead and propagate
through parseSingleColonScheme / parseDnsScheme.
Previously parseHostPort fell back to ParsedTarget(hostPort, null) when the port failed to parse, causing server.address to contain a trailing colon or invalid port fragment (e.g. "myhost:" or "myhost:abc"). Return ParsedTarget(host, null) instead so server.address holds just the host.
createClientRequest and createServerRequest are only called from TracingClientInterceptor / TracingServerInterceptor in the same package. Avoid widening the public API surface (and avoid exposing the internal ParsedTarget type via a public signature).
Comment on lines
+59
to
+63
| int slashIndex = rest.indexOf('/'); | ||
| if (slashIndex != -1) { | ||
| return new ParsedTarget(rest.substring(slashIndex), null); | ||
| } | ||
| return new ParsedTarget(rest, null); |
Comment on lines
+96
to
+132
| if (interceptWithTargetMethod != null && interceptorFactoryClass != null) { | ||
| try { | ||
| interceptWithTargetMethod.invoke(null, builder, newInterceptorFactory()); | ||
| return; | ||
| } catch (Exception e) { | ||
| logger.log(FINE, "Failed to use interceptWithTarget, falling back", e); | ||
| } | ||
| } | ||
|
|
||
| // Fallback for gRPC < 1.64.0: add interceptor without target info | ||
| builder.intercept(newTracingClientInterceptor(null)); | ||
| } | ||
|
|
||
| private Object newInterceptorFactory() { | ||
| // Proxies InternalManagedChannelBuilder$InternalInterceptorFactory, whose only declared | ||
| // method is newInterceptor(String target). Object methods get identity semantics — we have | ||
| // no natural delegate to forward them to. | ||
| return Proxy.newProxyInstance( | ||
| ManagedChannelBuilder.class.getClassLoader(), | ||
| new Class<?>[] {interceptorFactoryClass}, | ||
| (proxy, method, args) -> { | ||
| if ("newInterceptor".equals(method.getName())) { | ||
| return newTracingClientInterceptor((String) args[0]); | ||
| } | ||
| switch (method.getName()) { | ||
| case "equals": | ||
| return proxy == args[0]; | ||
| case "hashCode": | ||
| return System.identityHashCode(proxy); | ||
| case "toString": | ||
| return "GrpcInterceptorFactory"; | ||
| default: | ||
| throw new UnsupportedOperationException(method.toString()); | ||
| } | ||
| }); | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Currently built on top of
Prototype for open-telemetry/semantic-conventions#3317
Java agent instrumentation can do this for all gRPC versions.
Library instrumentation can only do this (automatically) using an internal gRPC API available since version 1.64.0: InternalManagedChannelBuilder.
I'm not convinced about the API / dependency on internals, could limit us on marking it stable, but marking ready-for-review to get feedback.
Related to #15871